//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/*
------------------------------------------------------------------------------
    Revision History
    Date                Author          Activity ID              Activity Headline
    2007-04-28          qinxuetao       WM600003778              Enable PPC to restore state after SIM reset
------------------------------------------------------------------------------
*/

#include <precomp.h>

#ifdef SIMULATE_HUNG_RADIO
BOOL g_fSimulateHungRadio = FALSE;
#endif


#ifdef RIL_RADIO_RESILIENCE

char g_szOpSelCmd[NETWK_CMDBUF_LENGTH];
BOOL g_fOperatorSelected = FALSE;
char g_szPBLocCmd[MISC_CMDBUF_LENGTH_PBLOC];
BOOL g_fPBLocationSet = FALSE;
extern DWORD   g_dwCacheDownlinkVolume;
extern DWORD   g_dwCacheUplinkVolume;
CRITICAL_SECTION g_csReboot;
BOOL g_fInitedFirstTime = FALSE;

extern BOOL g_bRadioOff;

SYSTEMTIME g_LastResetTime;
BOOL g_fRadioResetRecently =FALSE;
UINT g_cbLastLockCapsSize = 0;
void *g_pLastLockCaps = NULL;

#define SECONDS_TO_100NS(SEC) (((UINT64)(SEC)) * 10000000ui64)
#define RESET_SPOOF_DURATION SECONDS_TO_100NS(10)

#ifdef OEM1_DRIVER
#include <csmi_api.h>
#endif

#ifdef OEM2_DRIVER
#define IOCTL_TTPCOM_REBOOT CTL_CODE( FILE_DEVICE_SERIAL_PORT, 2048, METHOD_BUFFERED, FILE_ANY_ACCESS)
#endif // OEM2_DRIVER

extern void FillEquipmentStateStruct(RILEQUIPMENTSTATE * pres, DWORD dwEqState);

BOOL ShouldSpoofCommand()
{

    BOOL fReturn = FALSE;
    if (g_fRadioResetRecently)
    {
        SYSTEMTIME CurrentSystemTime;
        GetSystemTime(&CurrentSystemTime);
        UINT64 CurrentFileTime;
        UINT64 LastResetFileTime;
        if ((SystemTimeToFileTime(&CurrentSystemTime, (LPFILETIME)&CurrentFileTime)) &&
            (SystemTimeToFileTime(&g_LastResetTime, (LPFILETIME)&LastResetFileTime)))
        {
            if ((CurrentFileTime - LastResetFileTime) < RESET_SPOOF_DURATION)
            {
                fReturn = TRUE;  
            }
            else
            {
                // We've passed the time where we are comfortable spoofing this response.
                // set the flag so that we won't check next time
                g_fRadioResetRecently = FALSE;
            }
        }
    }
    return fReturn;
}

BOOL RebootRadio()
{
    FUNCTION_TRACE(RebootRadio);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : CRilHandle::RebootRadio\r\n")));

    BOOL bReset = FALSE;;

#ifdef SIMULATE_HUNG_RADIO
    g_fSimulateHungRadio = FALSE;
#endif

    // Add platform specific code to reboot the radio here
#ifdef OEM1_DRIVER
    TCHAR tszComName[6];
    // Get the COM port name
    if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ControlPort"), tszComName, 6))
    {
        _tcsncpyz(tszComName, TEXT("GTI1:"), 6);
    }
    
    // Try to open the COM port
    HANDLE hRebootPort = CreateFile(tszComName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);

    if (hRebootPort != INVALID_HANDLE_VALUE)
    {
        DWORD dwBytesReturned = 0;
#ifdef SIMULATE_HUNG_RADIO
        g_fSimulateHungRadio = FALSE;
#endif
        bReset = (DeviceIoControl(hRebootPort, GTI_IOCTL_GC_BOOT, NULL, 0, NULL, 0, &dwBytesReturned, NULL) != 0);        
        (void)CloseHandle(hRebootPort);
        bReset = TRUE;
    }
#endif // OEM1_DRIVER

#ifdef OEM2_DRIVER
    TCHAR tszComName[6];
    if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("ComPort"), tszComName, 6))
    {
        _tcsncpyz(tszComName, TEXT("MUX1:"), 6);
    }

    // Try to open the COM port
    HANDLE hRebootPort = CreateFile(tszComName, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
    if (INVALID_HANDLE_VALUE != hRebootPort)
    {
        DWORD dwBytesReturned = 0;
#ifdef SIMULATE_HUNG_RADIO
        g_fSimulateHungRadio = FALSE;
#endif
        DeviceIoControl(hRebootPort, IOCTL_TTPCOM_REBOOT, NULL, 0, NULL, 0,  &dwBytesReturned, NULL);
        CloseHandle(hRebootPort);
        bReset = TRUE;
    }
#endif // OEM2_DRIVER
    return bReset;
}

#if defined(OEM1_DRIVER) && defined(IMPLEMENT_DAR_REPORTING)
static HRESULT ParseDAR(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    TBD_FUNCTION(ParseDAR);

#if RIL_WATSON_REPORT    
    g_fWatsonCalled = FALSE;
#endif // RIL_WATSON_REPORT
    SignalCriticalError(RILLOG_EVENT_RADIOFAILUREREASON, __LINE__, __FILE__);        
    return S_OK;
}
#endif

#ifndef WAVECOM_DRIVER
BOOL RebootRestoreState()
{
    BOOL fSuccess = FALSE;
    char szNextCmd[MAX_PATH];
    RILEQUIPMENTSTATE res;    
    CNotificationData *pnd = NULL;
    DWORD dwDisconnectReason = RIL_DISCINIT_REMOTE;
    DWORD dwOldReadyState = g_dwReadyState;
    BOOL OldRadioOff = g_bRadioOff;
    EnterCriticalSection(&g_csReboot);

    RETAILMSG(MSG_ON,(TEXT("Function: RebootRestoreState...\r\n")));
    // Audio control: Make sure that we turn off the audio path both starting up
    IndicateCallActivityToAudioSubsystem ( FALSE, FALSE );

    // We rebooted, so we want to cycle through the ready states from none again.
    g_dwReadyState = RIL_READYSTATE_NONE;        

    // Test hook for knowing when the radio rebooted
    pnd = new CNotificationData;
    if (pnd && !pnd->InitFromRealBlob(RIL_NOTIFY_RADIORESET, NULL, 0))
    {
        delete pnd;
        pnd = NULL;
    }
    else
    {
        QueueCmdIgnoreRsp(APIID_NONE, NULL, CMDOPT_INIT | CMDOPT_NOOP, g_TimeoutCmdInit, NULL, pnd, 0, 0, 0);
    }
    // Assume any calls were disconnected
    pnd = new CNotificationData;
    if (pnd && !pnd->InitFromRealBlob(RIL_NOTIFY_DISCONNECT, (void*)&dwDisconnectReason, sizeof(dwDisconnectReason)))
    {
        delete pnd;
        pnd = NULL;
    }
    else
    {
        QueueCmdIgnoreRsp(APIID_NONE, NULL, CMDOPT_INIT | CMDOPT_NOOP, g_TimeoutCmdInit, NULL, pnd, 0, 0, 0);
    }

    SendComInitString(COM_INIT_INDEX);
            
    if (OldRadioOff == FALSE)
    {
        FillEquipmentStateStruct(&res, RIL_EQSTATE_MINIMUM);
        pnd = new CNotificationData;
        if (pnd && !pnd->InitFromRealBlob(RIL_NOTIFY_RADIOEQUIPMENTSTATECHANGED, (void*)&res, res.cbSize))
        {
            delete pnd;
            pnd = NULL;
        }
    }
    else
    {
        // Don't send a CFUN 0 notification if the radio was already off
        pnd = NULL;
    }
   
    // Cycle the radio equipment state, otherwise OEM1 radio will return lots of CME ERROR:3
    QueueCmdIgnoreRsp(APIID_SETEQUIPMENTSTATE, "AT+CFUN=0\r", CMDOPT_INIT, g_TimeoutCmdInit, NULL, pnd, 0, 0, 0);

    Sleep(2000);
    if (OldRadioOff == FALSE)
    {
        FillEquipmentStateStruct(&res, RIL_EQSTATE_FULL);
        pnd = new CNotificationData;
        if (pnd && !pnd->InitFromRealBlob(RIL_NOTIFY_RADIOEQUIPMENTSTATECHANGED, (void*)&res, res.cbSize))
        {
            delete pnd;
            pnd = NULL;
        }
        QueueCmdIgnoreRsp(APIID_SETEQUIPMENTSTATE, "AT+CFUN=1\r", CMDOPT_INIT | CMDOPT_SETRADIOON | CMDOPT_IGNORERADIOOFF | CMDOPT_REINIT, g_TimeoutCmdInit, NULL, pnd, 0, 0, 0);
    }
    
    // if we just rebooted and were previously unlocked, then unlock the phone
    if (dwOldReadyState & RIL_READYSTATE_UNLOCKED)
    {
        CHAR szLastPIN[256];
        if (FetchPINSecure(szLastPIN, sizeof(szLastPIN)))
        {
            (void)_snprintfz(szNextCmd, MAX_PATH, "AT+CPIN=\"%s\"\r", szLastPIN);
            QueueCmdIgnoreRsp(APIID_NONE, szNextCmd, CMDOPT_INIT | CMDOPT_UNLOCKING, g_TimeoutCmdInit, NULL, NULL, 0, 0, 0);
        }
    }    

    //Sleep(4000);
    if (g_fOperatorSelected && (OldRadioOff == FALSE))
    {
        // if we just rebooted and were previously registered, then reregister on the network
        QueueCmdIgnoreRsp(APIID_NONE, g_szOpSelCmd, CMDOPT_INIT, g_TimeoutCmdInit, NULL, NULL, 0, 0, 0);
    }

    // Restore audio gain
    if(g_dwCacheDownlinkVolume)
    {
        (void)_snprintfz(szNextCmd, MAX_PATH, "AT+VGR=%u\r", ConvertAudioRxGain(g_dwCacheDownlinkVolume, TRUE));
        QueueCmdIgnoreRsp(APIID_NONE, szNextCmd, CMDOPT_INIT, g_TimeoutCmdInit, NULL, NULL, 0, 0, 0);
    }
    // Restore PB location
    if (g_fPBLocationSet)
    {
        QueueCmdIgnoreRsp(APIID_NONE, g_szPBLocCmd, CMDOPT_INIT, g_TimeoutCmdInit, NULL, NULL, 0, 0, 0);
    } 
    fSuccess = TRUE;

    LeaveCriticalSection(&g_csReboot);
    return fSuccess;
}
#else
BOOL RebootRestoreState()
{
    BOOL fSuccess = FALSE;
    char szNextCmd[MAX_PATH];
    RILEQUIPMENTSTATE res;    
    CNotificationData *pnd = NULL;
    DWORD dwDisconnectReason = RIL_DISCINIT_REMOTE;
    DWORD dwOldReadyState = g_dwReadyState;
    BOOL OldRadioOff = g_bRadioOff;
    EnterCriticalSection(&g_csReboot);


    // Audio control: Make sure that we turn off the audio path both starting up
    IndicateCallActivityToAudioSubsystem ( FALSE, FALSE );

    // We rebooted, so we want to cycle through the ready states from none again.
    g_dwReadyState = RIL_READYSTATE_NONE;        

    // Test hook for knowing when the radio rebooted
    pnd = new CNotificationData;
    if (pnd && !pnd->InitFromRealBlob(RIL_NOTIFY_RADIORESET, NULL, 0))
    {
        delete pnd;
        pnd = NULL;
    }
    else
    {
        QueueCmdIgnoreRsp(APIID_NONE, NULL, CMDOPT_INIT | CMDOPT_NOOP, g_TimeoutCmdInit, NULL, pnd, 0, 0, 0);
    }
    // Assume any calls were disconnected
    pnd = new CNotificationData;
    if (pnd && !pnd->InitFromRealBlob(RIL_NOTIFY_DISCONNECT, (void*)&dwDisconnectReason, sizeof(dwDisconnectReason)))
    {
        delete pnd;
        pnd = NULL;
    }
    else
    {
        QueueCmdIgnoreRsp(APIID_NONE, NULL, CMDOPT_INIT | CMDOPT_NOOP, g_TimeoutCmdInit, NULL, pnd, 0, 0, 0);
    }
#ifdef OEM2_DRIVER
    // Seems like the OEM2 radio needs a bit before we send down the Init strings.
    Sleep(500);
#endif // OEM2_DRIVER

    SendComInitString(COM_INIT_INDEX);
        
#if defined(OEM1_DRIVER) && defined(IMPLEMENT_DAR_REPORTING)
    // OEM1 uses %DAR to query the cause of the last radio crash.  We want this in the radio log or watson report
    QueueCmdIgnoreRsp(APIID_NONE, "AT%DAR\r", CMDOPT_INIT, g_TimeoutCmdInit, ParseDAR, NULL, 0, 0, 0);
#endif
            
    if (OldRadioOff == FALSE)
    {
        FillEquipmentStateStruct(&res, RIL_EQSTATE_MINIMUM);
        pnd = new CNotificationData;
        if (pnd && !pnd->InitFromRealBlob(RIL_NOTIFY_RADIOEQUIPMENTSTATECHANGED, (void*)&res, res.cbSize))
        {
            delete pnd;
            pnd = NULL;
        }
    }
    else
    {
        // Don't send a CFUN 0 notification if the radio was already off
        pnd = NULL;
    }
   
    // Cycle the radio equipment state, otherwise OEM1 radio will return lots of CME ERROR:3
    QueueCmdIgnoreRsp(APIID_SETEQUIPMENTSTATE, "AT+CFUN=0\r", CMDOPT_INIT, g_TimeoutCmdInit, NULL, pnd, 0, 0, 0);

    if (OldRadioOff == FALSE)
    {
        FillEquipmentStateStruct(&res, RIL_EQSTATE_FULL);
        pnd = new CNotificationData;
        if (pnd && !pnd->InitFromRealBlob(RIL_NOTIFY_RADIOEQUIPMENTSTATECHANGED, (void*)&res, res.cbSize))
        {
            delete pnd;
            pnd = NULL;
        }
#ifdef OEM1_DRIVER
        // We must send the terminal profile string along when we turn on the radio
        CHAR szCmdBuf[MAX_PATH];
        _snprintfz(szCmdBuf, ARRAY_LENGTH(szCmdBuf), "AT%%SATC=1,%s;+CFUN=1\r", g_RilSTKTerminalProfile.GetText());
        QueueCmdIgnoreRsp(APIID_SETEQUIPMENTSTATE, szCmdBuf, CMDOPT_INIT | CMDOPT_SETRADIOON | CMDOPT_IGNORERADIOOFF | CMDOPT_REINIT, g_TimeoutCmdInit, NULL, pnd, 0, 0, 0);
#else
        QueueCmdIgnoreRsp(APIID_SETEQUIPMENTSTATE, "AT+CFUN=1\r", CMDOPT_INIT | CMDOPT_SETRADIOON | CMDOPT_IGNORERADIOOFF | CMDOPT_REINIT, g_TimeoutCmdInit, NULL, pnd, 0, 0, 0);
#endif // ! OEM1_DRIVER
    }
    
    // if we just rebooted and were previously unlocked, then unlock the phone
    if (dwOldReadyState & RIL_READYSTATE_UNLOCKED)
    {
        CHAR szLastPIN[256];
        if (FetchPINSecure(szLastPIN, sizeof(szLastPIN)))
        {
            (void)_snprintfz(szNextCmd, MAX_PATH, "AT+CPIN=\"%s\"\r", szLastPIN);
            QueueCmdIgnoreRsp(APIID_NONE, szNextCmd, CMDOPT_INIT, g_TimeoutCmdInit, NULL, NULL, 0, 0, 0);
        }
    }    

    if (g_fOperatorSelected && (OldRadioOff == FALSE))
    {
        // if we just rebooted and were previously registered, then reregister on the network
        QueueCmdIgnoreRsp(APIID_NONE, g_szOpSelCmd, CMDOPT_INIT, g_TimeoutCmdInit, NULL, NULL, 0, 0, 0);
    }

    // Restore audio gain
    (void)_snprintfz(szNextCmd, MAX_PATH, "AT+CLVL=%u\r", g_dwCacheDownlinkVolume);
    QueueCmdIgnoreRsp(APIID_NONE, szNextCmd, CMDOPT_NONE, g_TimeoutCmdInit, NULL, NULL, 0, 0, 0);

    // Restore PB location
    if (g_fPBLocationSet)
    {
        QueueCmdIgnoreRsp(APIID_NONE, g_szPBLocCmd, CMDOPT_INIT, g_TimeoutCmdInit, NULL, NULL, 0, 0, 0);
    } 
    fSuccess = TRUE;

    LeaveCriticalSection(&g_csReboot);
    return fSuccess;
}
#endif

HRESULT RILDrv_RebootRadio (DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_RebootRadio);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_RebootRadio\r\n")));

    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    if (!RebootRadio())
    {
        hr = E_NOTIMPL;
        goto Error;
    }

    // send an OK response
    if (!QueueCmd(pHandle, NULL, CMDOPT_NOOP, APIID_REBOOTRADIO, NULL, NULL, hr)) 
    {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

#endif // RIL_RADIO_RESILIENCE

